#include "cmdProcess.h"
#include <stdio.h>
#include "main.h"
#include "myTask.h"
#include "rc522.h"
#include "koroCard.h"

typedef struct{
    const char *cmdName;
    const char *brief;
    void (*pCmdCallback)(uint8_t argc, char *argv[]);
}cmdItem;

void cmd_cb_echo(uint8_t argc, char *argv[]);
void cmd_cb_help(uint8_t argc, char *argv[]);
void cmd_cb_hello(uint8_t argc, char *argv[]);
void cmd_cb_battery(uint8_t argc, char *argv[]);
void cmd_cb_ws2812_test(uint8_t argc, char *argv[]);
void cmd_cb_card(uint8_t argc, char *argv[]);
void cmd_cb_audio(uint8_t argc, char *argv[]);
void cmd_cb_volume(uint8_t argc, char *argv[]);
void cmd_cb_mesh(uint8_t argc, char *argv[]);

cmdItem cmdList[] = {
    {
        .cmdName = "echo",
        .brief = "return params to test uart",
        .pCmdCallback = cmd_cb_echo,
    },
    {
        .cmdName = "hello",
        .brief = "hello",
        .pCmdCallback = cmd_cb_hello,
    },
    {
        .cmdName = "battery",
        .brief = "print battery voltage and percentage",
        .pCmdCallback = cmd_cb_battery,
    },
    {
        .cmdName = "ws2812_test",
        .brief = "show some anime to test ws2812",
        .pCmdCallback = cmd_cb_ws2812_test,
    },
    {
        .cmdName = "card",
        .brief = "read or write card data",
        .pCmdCallback = cmd_cb_card,
    },
    {
        .cmdName = "audio",
        .brief = "play specified audio",
        .pCmdCallback = cmd_cb_audio,
    },
    {
        .cmdName = "volume",
        .brief = "set audio volume",
        .pCmdCallback = cmd_cb_volume,
    },
    {
        .cmdName = "mesh",
        .brief = "test mesh",
        .pCmdCallback = cmd_cb_mesh,
    },
    {
        .cmdName = "help",
        .brief = "print brief of commands",
        .pCmdCallback = cmd_cb_help,
    },
    {
        .cmdName = " ",
        .pCmdCallback = NULL,
    },
};

int my_str_cmp(const char *str1, char *str2){
    uint32_t i;
    for(i = 0; i < CMD_MAX_ARG_COUNTS; i++){
        if(str1[i] == '\0' || str1[i] == ' '){
            if(str2[i] == '\0' || str2[i] == ' ' || str2[i] == '\n'){
                str2[i] = '\0';
                return 0;
            }else {
                return -1;
            }
        }
        if(str1[i] != str2[i]){
            return -1;
        }
    }

    return 0;
}

void cmd_cb_echo(uint8_t argc, char *argv[]){
    if(argc > 1){
        if(my_str_cmp("-h", argv[1]) == 0){
            goto Useage_echo;
        }
        printf_DMA(PRINT_LOG"%s\n", argv[1]);
    }else {
        printf_DMA(PRINT_WARNNING"Need param to echo!\n");
    }
    return ;
Useage_echo:
    printf_DMA(PRINT_LOG"Useage: This function will return the first param to test uart\n");
}

void cmd_cb_hello(uint8_t argc, char *argv[]){
    printf_DMA(PRINT_LOG"Ciallo World~ (∠・ω< )⌒☆\n");
}

void cmd_cb_battery(uint8_t argc, char *argv[]){
    float vol;
    uint8_t pct;
    get_battery_voltage(&vol);
    pct = get_battery_percent();

    printf_DMA(PRINT_LOG"Voltage: %.2f, Percentage: %d\n", vol, pct);
}

void cmd_cb_ws2812_test(uint8_t argc, char *argv[]){

#define P_SHOW                  0
#define P_STOP                  1
#define P_LIST                  2

    uint8_t i, j;
    const char *paramName[] = {
        [P_SHOW] = "-show",
        [P_STOP] = "-stop",
        [P_LIST] = "-list",

        " ",
    };

    const char *animeName[] = {
        [KORO_A_CLEAR] = "clear",
        [KORO_A_BLINK] = "blink",
        [KORO_A_BREATHE] = "breathe",

        " ",
    };

    if(argc < 2){
        goto Useage_ws2812_test;
    }

    for(i = 0; paramName[i][0] != ' '; i++){
        if(my_str_cmp(paramName[i], argv[1]) == 0){
            switch(i){
                case P_SHOW:
                    if(argc != 3){
                        printf_DMA(PRINT_WARNNING"Please provide the correct animation name!\n");
                        goto Useage_ws2812_test;
                    }
                    for(j = 0; animeName[j][0] != ' '; j++){
                        if(my_str_cmp(animeName[j], argv[2]) == 0){
                            wsAnimeFlag = j + KORO_A_OFFSET; // 偏移 2

                            return ;
                        }
                    }
                    printf_DMA(PRINT_WARNNING"Please provide the correct animation name!\n");
                    goto Useage_ws2812_test;

                    break;

                case P_STOP:
                    wsAnimeFlag = 0;
                    break;
                    
                case P_LIST:
                    printf_DMA(PRINT_LOG"List of animations available for playback:\n");
                    for(j = 0; animeName[j][0] != ' '; j++){
                        printf_DMA("\t%s\n", animeName[j]);
                    }

                    break;
            }
            return ;
        }
    }

    printf_DMA(PRINT_WARNNING"Unknown param: %s\n", argv[1]);

Useage_ws2812_test:
    printf_DMA(PRINT_LOG"Useage:\n"
                PRINT_LOG"\t%s <-show/-stop/-list> [anime_name]\n", argv[0]);
}

void cmd_cb_card(uint8_t argc, char *argv[]){
    struct koroCardDataStu cardData;
    if(argc < 2){
        printf_DMA(PRINT_WARNNING"Missing param\n");
        goto Useage_card;
    }

    switch(argv[1][1]){
        case 'r':
            switch(koro_read_card(&myKoro, &cardData)){
                case KORO_OK:
                    printf_DMA(PRINT_LOG"Read koro card data: id: %d, maddr: 0x%04x, meshData: 0x%x\n", cardData.id, cardData.meshAddrU.meshADDR_16, cardData.meshData);
                    printf_DMA(PRINT_LOG"\taudioFolder: %d, audioIndex: %d\n", cardData.audioFolder, cardData.audioIndex);
        
                    break;
                    
                case KORO_ERR:
                    printf_DMA(PRINT_WARNNING"Read card ERROR!\n");
        
                    break;
                
                case KORO_NOCARD:
                    printf_DMA(PRINT_WARNNING"No card detected!\n");
        
                    break;
                    
                case KORO_NOTKORO:
                    printf_DMA(PRINT_WARNNING"This is not koro card!\n");
        
                    break;
        
                default:
                    break;
        
            }

            break;
            
        case 'w':
            if(argc != 7){
                printf_DMA(PRINT_WARNNING"Please enter the koro card data correctly\n");
                goto Useage_card;
            }
            sscanf(argv[2], "%d", &cardData.id);
            sscanf(argv[3], "%x", &cardData.meshAddrU.meshADDR_16);
            sscanf(argv[4], "%x", &cardData.meshData);
            sscanf(argv[5], "%d", &cardData.audioFolder);
            sscanf(argv[6], "%d", &cardData.audioIndex);
            if(cardData.meshAddrU.meshADDR_16 > 0xFF00 || cardData.meshAddrU.meshADDR_16 < 0x0006){
                if(cardData.meshAddrU.meshADDR_16 != 0xFFFF){
                    printf_DMA(PRINT_WARNNING"Target device mesh addr not in range\n");
                    goto Useage_card;
                }
            }
            if(cardData.audioFolder > 99 || cardData.audioFolder < 1){
                printf_DMA(PRINT_WARNNING"Audio folder not in range\n");
                goto Useage_card;
            }
            switch(koro_write_card(&myKoro, &cardData)){
                case KORO_OK:
                    printf_DMA(PRINT_LOG"Write koro card data Okay.\n");

                    break;

                default:
                    printf_DMA(PRINT_ERROR"Write koro card data Failed!\n");
                    break;
            }
            break;
        
        case 'h':
            goto Useage_card;

            break;

        default:
            printf_DMA(PRINT_WARNNING"Unknown option: %s\n", argv[1]);
            goto Useage_card;
            break;
    }

    return ;

Useage_card:
    printf_DMA(PRINT_LOG"Useage: %s <-r/-w> [id(DEC)] [mAddr(HEX)] [meshData(HEX)] [audioFolder(DEC)] [audioIndex(DEC)]\n", argv[0]);
    printf_DMA(PRINT_LOG"\t-r: read card data\n"
                PRINT_LOG"\t-w: write card data\n"
                PRINT_LOG"\t\tid: 0~255\n"
                PRINT_LOG"\t\tmAddr: 0x0006~0xFF00, 0xFFFF\n"
                PRINT_LOG"\t\tmeshData: 0x00~0xFF\n"
                PRINT_LOG"\t\taudioFolder: 1~99\n"
                PRINT_LOG"\t\taudioIndex: 0~255\n");
}

void cmd_cb_audio(uint8_t argc, char *argv[]){
    uint8_t folderIndex, audioIndex;
    if(argc != 3){
        goto Useage_audio;
    }

    sscanf(argv[1], "%d", &folderIndex);
    sscanf(argv[2], "%d", &audioIndex);

    ch_play_folder(&myCh, folderIndex, audioIndex);

    return ;

Useage_audio:
    printf_DMA(PRINT_LOG"Useage: %s <folderIndex> <auioIndex>\n", argv[0]);
}

void cmd_cb_volume(uint8_t argc, char *argv[]){
    uint8_t volSet;
    if(argc < 2){
        goto Useage_volume;
    }

    sscanf(argv[1], "%d", &volSet);

    ch_set_vol(&myCh, 1, volSet);
    printf_DMA(PRINT_LOG"Set volume over.\n");

    return ;

Useage_volume:
    printf_DMA(PRINT_LOG"Useage: %s <volume(0~30)>\n", argv[0]);
}

void cmd_cb_mesh(uint8_t argc, char *argv[]){
    GPIO_PinState status = HAL_GPIO_ReadPin(myJdy.statPort, myJdy.statPin);
    uint8_t i;
    uint8_t meshData[10];
    uint16_t mAddr;
    if(argc < 2){
        goto Useage_mesh;
    }

    switch(argv[1][2]){
        case 't':
            printf_DMA(PRINT_LOG"Jdy stat Pin: %d\n", status);
            break;

        case 'e':
            // 开启中心机 stat 引脚才有效
            // if(status == 0){
            //     printf_DMA(PRINT_WARNNING"Mesh not connected!\n");
            //     return ;
            // }
            if(argc < 4){
                goto Useage_mesh;
            }else {
                if(argc > 7){
                    printf_DMA(PRINT_WARNNING"Too many msg!\n");
                    return ;
                }
                sscanf(argv[2], "%x", &mAddr);
                for(i = 0; i < (argc - 3); i ++){
                    sscanf(argv[3 + i], "%x", &(meshData[i]));
                }
                jdy25m_mesh_send(&myJdy, mAddr, meshData, (argc - 3), JDY25M_MESH_CMD_NOACK);
            }
            

            break;

        default:
            goto Useage_mesh;
            break;
    }

    return ;

Useage_mesh:
    printf_DMA(PRINT_LOG"Useage: %s <-status/-send> [maddr(HEX)] [message0(HEX)] ...(max: msg3)\n", argv[0]);
}

void cmd_cb_help(uint8_t argc, char *argv[]){
    uint8_t i;
    
    if(argc > 1){
        for(i = 0; cmdList[i].cmdName[0] != ' '; i ++){
            if(my_str_cmp(cmdList[i].cmdName, argv[1]) == 0){
                printf_DMA(PRINT_LOG"%s   -   %s\n", cmdList[i].cmdName, cmdList[i].brief);
                return;
            }
        }
    }else {
        for(i = 0; cmdList[i].cmdName[0] != ' '; i ++){
            printf_DMA(PRINT_LOG"%s:\n\t%s\n", cmdList[i].cmdName, cmdList[i].brief);
        }

        return ;
    }

    printf_DMA(PRINT_WARNNING"Unknown cmd: %s, Type /help to list all commands\n", argv[1]);
}


void process_command(char *cmd){
    uint32_t i = 0;
    uint8_t index = 1;
    char *argv[CMD_MAX_ARG_COUNTS] = {
        [0] = cmd + 1,
    };

    if(!(cmd[0] == '/' || cmd[0] == '#')){ // 非指令
        printf_DMA(PRINT_WARNNING"Unknow format!\n");
        // printf_DMA(PRINT_DEBUG"%s\n", cmd);
        return;
    }

    for(i = 1; (cmd[i] != '\0') && (i < U1BUFFER_SIZE - 1) && (index < CMD_MAX_ARG_COUNTS - 1); i ++){
        if((cmd[i-1] == ' ') && (cmd[i] != ' ')){
            argv[index] = &cmd[i];
            index ++;
            cmd[i-1] = '\0';
        }
    }

    for(i = 0; cmdList[i].cmdName[0] != ' '; i ++){
        if(my_str_cmp(cmdList[i].cmdName, argv[0]) == 0){
            if(cmdList[i].pCmdCallback != NULL){
                argv[0] = cmd;
                cmdList[i].pCmdCallback(index, argv);
            }else {
                printf_DMA(PRINT_ERROR"Callback function of \"%s\" is not define!\n", argv[0]);
            }
            return;
        }
    }

    printf_DMA(PRINT_WARNNING"Unknown cmd: %s\n", argv[0]);
    printf_DMA(PRINT_LOG"Type /help to list all commands\n");
}
